package dsc;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.logging.Logger;

/**
 * De messagehandler haalt de messages uit de queue en kijkt wat hij hiermee
 * moet doen. Hiermee roept hij de goede methode aan in de message creator die
 * dan de message opstuurt naar de message gespecificeerde device in uit de
 * datastorage. Enige uitzondering is de control server die niet in de
 * datastorage is opgenomen.
 */
public class MessageHandler extends Thread {

    public static InetAddress csAddress;
    public static String serverident;
    Vector<DatagramPacket> messageQueue = new Vector<DatagramPacket>();
    MessageCreator creator;
    static Logging logInstance = new Logging(MessageHandler.class.getName());
    static Logger logger;
    DataStorage ds;
    int messagecounter = 0;
    Clients client;
    byte[] msgarray;
    ConnectedDevices device;
    InetAddress address;
    String msg, name, password, temp;
    String[] msgSplit;
    int port;
    List list;

    public MessageHandler(MessageCreator creator, DataStorage ds) {
        logger = logInstance.createLogger();

        try {
            logInstance.init(logger);
        } catch (Exception e) {
        }

        this.creator = creator;
        this.ds = ds;

    }

    /**
     * Converteer byte array naar een String
     *
     * @param msgarray
     *            in bytes
     * @return msg
     */
    public static synchronized String byteArrayToString(byte[] msgarray) {
        String msg = "";

        for (int i = 0; i < msgarray.length; i++) {
            msg += Character.toString((char) msgarray[i]);
        }
        return msg;
    }

    /**
     * Voeg berichten toe aan de te versturen message queue
     *
     * @param message
     * @throws Exception 
     */
    public synchronized void addMessageToMessageQueue(DatagramPacket message)
            throws Exception {

        messageQueue.add(message);
        notify();
    }

    /**
     * Haal berichten uit de message queue op
     *
     * @return message
     * @throws InterruptedException
     */
    public synchronized DatagramPacket getMessageFromMessageQueue()
            throws InterruptedException {
        while (messageQueue.isEmpty()) {
            wait();
        }

        DatagramPacket message = (DatagramPacket) messageQueue.get(0);
        logger.info("De message die ik eruit haal "
                + byteArrayToString(getMessageFromDatapacket(message)));
        messageQueue.removeElementAt(0);
        return message;
    }

    /**
     * Datapacket uitpakken om bericht op te halen
     *
     * @param packet
     * @return byte []
     */
    public static synchronized byte[] getMessageFromDatapacket(DatagramPacket packet) {
        byte[] data = packet.getData();
        int messagelength = ((data[0] << 8) & 0xff00) | (data[1] & 0xff);

        /*
         *-6 omdat dat niet bij het bericht hoort.
         */
        byte[] message = new byte[messagelength - 6];
        for (int i = 0; i < message.length; i++) {
            message[i] = data[i + 6];
        }

        return message;
    }
    private synchronized void sendToAllAdmins(int messagetype, String msg){
        try{
                list = ds.getAdmins();
            for(int i = 0; i < list.size(); i++){
                client = (Clients) list.get(i);
                if(messagetype == 200 || messagetype == 300){
                    creator.msg300(ds.getDevice(client.getIpadress(),
                            client.getPort()), msg.getBytes());
                }
                else{
                    creator.msg310(ds.getDevice(client.getIpadress(),
                            client.getPort()), msg.getBytes());
                }         
            }
        }catch(Exception e){
            e.printStackTrace();
            logger.severe("Er is iets fout gegaan in SendToAllAdmins");
        }
    }

    /**
     * Handelt berichten af op basis van berichttype
     *
     * @param messagetype
     * @param packet
     * @return boolean
     * @throws Exception
     */
    private synchronized boolean handle(int messagetype, DatagramPacket packet)
            throws Exception {

        /*
         * elk van de messages moeten de bytes anders geinterpreteerd worden ik
         * denk dat de getallen die in bercihten worden gegeven ook chars en
         * geen integers zou ook niet erg logisch zijn als ze intergers zijn.
         */
        byte[] msgarray;
        msgarray = getMessageFromDatapacket(packet);
        msg = byteArrayToString(msgarray);

        logger.info("Messagetype: " + messagetype + " Ontvangen bericht: "
                + msg + " Adres van het bericht: " + packet.getAddress());

        switch (messagetype) {
            /* Client registreert zich */
            case 100:
                return msg100(msg, msgarray, packet);

            /* Client toegevoegd */
            case 110:
                return msg110(msg, msgarray, packet);

            /* Client verwijderen */
            case 120:
                return msg120(msg, msgarray, packet);

            /* Client dood */
            case 130:
                return msg130(msg, msgarray, packet);

            /* Ping binnen, stuur pong */
            case 140:
                return msg140(msg, msgarray, packet);

            /* Sla pong op */
            case 150:
                return msg150(msg, msgarray, packet);

            /* Wijzig naam */
            case 160:
                return msg160(msg, msgarray, packet);

            /* Wijzig naam */
            case 170:
                return msg170(msg, msgarray, packet);

            /* Tekst bericht van client */
            case 200:
            /* Actie bericht van client */
            case 210:
                return msg2x0(messagetype, msg, msgarray, packet);

            case 300:
            /* Tekstbericht server om door te sturen aan server of client */
            case 310:
                return msg3x0(messagetype, msg, msgarray, packet);

            /* Een kind server wilt aan me hangen. */
            case 600:
                return msg600(msg, msgarray, packet);

            case 602:
                return msg602(msg, msgarray, packet);

            /* Hergroepeer actie. */
            case 604:
                return msg604(msg, msgarray, packet);
            /*Kill command*/
            case 700:
                return msg700(msg, msgarray, packet);

            /* Server stopt */
            case 701:
                return msg701(msg, msgarray, packet);

            case 799:
                logger.severe("Foutmelding 799");
                return true;

            default:
                logger.severe("Ongeldig berichttype");
                return false;
        }
    }

    /**
     * Handelt berichten van type 100. Dit is het registreren van een Client
     * en die client bekend maken aan de rest van het netwerk
     *
     * @param msg
     * @param msgarray
     * @param packet
     * @return boolean
     * @throws Exception
     */
    public boolean msg100(String msg, byte[] msgarray, DatagramPacket packet) {
        msgSplit = msg.split("\\s+");
        address = packet.getAddress();
        port = packet.getPort();
        String temp;
        /*maak de connectie aan waar je de client kan vinden */
        device = new ConnectedDevices(address, port);

        /* Client mag niet al bestaan */
        if (!ds.checkDuplicateNames(msgSplit[0]) || msgSplit[0].isEmpty()) {
            client = new Clients(device, msg);

            try {
                /*
                 * Er alleen een naam wordt gegeven dus niet als
                 * admin inloggen
                 */
                if (msgSplit.length == 1) {
                    /* De device en de client in de arrays */
                    ds.insertDevice(device);
                    ds.insertClient(client);
                    client.setStatus(true);
                    /* Stuur de client een succes begricht. */
                    creator.msg500(ds.getDevice(client.getIpadress(),
                            client.getPort()), "Het is gelukt!".getBytes());
                    logger.info("Client toegevoegd: " + client.getName());

                    /* Stuur clients in netwerk naar nieuwe client
                     * haal alle clients op
                     */
                    list = ds.getNonAdminClients();
                    logger.info("Ik ga al mijn clients sturen naar mijn client");
                    /*stuur alles clients die je hebt op naar de client.*/
                    for (int i = 0; i < list.size(); i++) {
                        Clients tClient = (Clients) list.get(i);

                        temp = String.format("%06d", list.size());
                        temp = String.format("%06d", i + 1) + temp
                                + tClient.getName();
                        creator.msg110(device, temp.getBytes());
                    }


                    logger.info("Stuur de client naar alle devices behalve"
                            + "de client zelf");
                    /* Stuur client naar alle apparaten
                     * haal alle devices op die er aan mij vast zitten.
                     */
                    list = ds.getDevices();
                    for (int i = 0; i < list.size(); i++) {
                        ConnectedDevices tdevice = (ConnectedDevices) list.get(i);

                        /*
                         * Als zender en ontvanger hetzelfde zijn, doe niets
                         * want anders komt hij twee keer in de lijst.
                         */
                        if (tdevice.equals(device)) {
                            continue;
                        }
                        temp = String.format("%06d", 1);
                        temp = String.format("%06d", 1) + temp
                                + client.getName();
                        creator.msg110(tdevice, temp.getBytes());
                    }
                    /* Voor de admin. */
                    
                } else if (msgSplit.length == 2) {
                    String[] admin;
                    password = msgSplit[1];

                    for (int i = 0; i < ds.admins.size(); i++) {
                        admin = ds.admins.get(i).split("\\s");
                        /* Check of het wachtwoord klopt*/
                        if (admin[0].matches(msgSplit[0])
                                && admin[1].matches(password)) {
                            /*
                             * Als het klopt dan voegen we het toe en
                             * zetten de adminveld op true.
                             */
                            device = new ConnectedDevices(address, port);
                            ds.insertDevice(device);
                            client = new Clients(device, msgSplit[0]);
                            client.setAdmin(true);
                            client.setStatus(true);
                            ds.insertClient(client);
                            /*haal alle niet admin clients op*/
                            list = ds.getClients();
                            /*Stuur het bericht op dat het gelukt is.*/
                            creator.msg500(ds.getDevice(client.getIpadress(),
                                    client.getPort()), "Het is gelukt!".getBytes());
                            logger.info("Stuur de clients naar de admin");
                            /*stuur alle clienten op naar de admin*/
                            for (int j = 0; j < list.size(); j++) {
                                Clients tClient = (Clients) list.get(j);
                                temp = String.format("%06d", list.size());
                                temp = String.format("%06d", j + 1) + temp
                                        + tClient.getName();
                                creator.msg110(ds.getDevice(
                                        client.getIpadress(), client.getPort()),
                                        temp.getBytes());
                            }
                            return true;
                        }
                    }
                    /* Username - password ongeldig, aanmelden mislukt! */
                    creator.msg510(device,
                            "Username/Password combinatie is ongeldig".getBytes());


                } else {
                    logger.severe("no name given with the 100msg");
                }
            } catch (Exception e) {
                logger.severe("Error in handle100: " + e.getMessage());
            }

            return true;
        } else {
            creator.msg510(device, "Naam bestaat al!".getBytes());
        }

        return true;
    }

    /**
     * Handelt berichten van type 110. Dit is het registeren van clienten die al
     * bekend zijn bij een server.
     *
     * @param msg
     * @param msgarray
     * @param packet
     * @return boolean
     * @throws Exception
     */
    public boolean msg110(String msg, byte[] msgarray, DatagramPacket packet) {
        int msglength = msg.length();
        int vnummer;
        int llength;
        /* Haal de lijst nummers op */


        address = packet.getAddress();
        port = packet.getPort();

        /* Haal de naam op */
        name = msg.substring(12);

        try {
            /* Check of je de naam al hebt. */
            if (!ds.checkDuplicateNames(name)) {
                /* Als de naam niet empty is. */
                vnummer = Integer.parseInt(msg.substring(0, 6).trim());
                llength = Integer.parseInt(msg.substring(6, 12).trim());
                if (!name.isEmpty()) {
                    logger.info("Client toevoegen met naam: " + name);
                    /* Add de naam uit tot de data storage. als het goed
                     * is dan is er al een device want je krijgt alleen
                     * 110 van servers.
                     */
                    client = new Clients(ds.getDevice(address, port), name);
                    ds.insertClient(client);
                    logger.info(client.getName());
                    /* Haal alle devices op en stuur het nelse et gekregen 
                     * bericht door naar de rest van de connected devices.
                     */
                    list = ds.getActiveDevices();
                    for (int i = 0; i < list.size(); i++) {
                        device = (ConnectedDevices) list.get(i);

                        /* Niet naar afzender sturen */
                        if (device.equals(ds.getDevice(address, port))) {
                            continue;
                        }
                        creator.msg110(device, msg.getBytes());
                    }
                }
                if(vnummer == llength){
                    /*
                     * Activeer de clienten pas wanneer ze allemaal ontvangen
                     * zijn
                     */
                    logger.severe("Ik activeer de clienten nu");
                    ds.activateClients();
                }
            } else {
                /* Als je de naam hebt gooi de server eruit. */
                logger.severe("Client bestaat al!");
                creator.msg603(Main.csAddress, 2001, ds.getDevice(address,
                        port).getTag().getBytes());
            }
        } catch (Exception e) {
            logger.severe("Apparaat niet beschikbaar, handle110");
        }

        return true;
    }

    /**
     * Handelt berichten van type 120. Dit is het verwijderen van een client die
     * direct verbonden is met de server en daarna bekent maken bij
     * de rest van de server.
     *
     * @param msg
     * @param msgarray
     * @param packet
     * @return boolean
     */
    public boolean msg120(String msg, byte[] msgarray, DatagramPacket packet) {
        try {
            /* Dit kun je alleen krijgen van een client
             * haal de connectie en de client op. om ze te verwijderen.
             */
            device = ds.getDevice(packet.getAddress(), packet.getPort());
            client = ds.getClient(device.getIpadress(), device.getPort());
            ds.deleteClient(client);
            ds.deleteDevice(device);
            list = ds.getDevices();

            msgSplit = msg.split("\\s+", 2);

            /* Als de client geen vaarwel bericht heeft gestuurd.
             * maken we zelf een bericht aan.
             */
            if (msgSplit.length <= 1) {
                msg = client.getName() + " Client is dood!";
            } else {
                msg = client.getName() + " " + msg;
            }

            /*
             * Stuur het bericht door zoals hij is naar
             * alle devices die er nog over zijn.
             */
            for (int i = 0; i < list.size(); i++) {
                device = (ConnectedDevices) list.get(i);
                creator.msg130(device, msg.getBytes());
            }

        } catch (Exception e) {
            logger.severe("Client niet beschikbaar.");
        }

        return true;
    }

    /**
     * Handelt berichten van type 130. Dit is verwijderd clienten die ergens
     * anders in het netwerk zijn gestopt.
     *
     * @param msg
     * @param msgarray
     * @param packet
     * @return boolean
     */
    public boolean msg130(String msg, byte[] msgarray, DatagramPacket packet) {
        msgSplit = msg.split("\\s+", 2);

        try {
            /* Zoek de client op in de array en verwijder hem.
             */
            if (ds.getDevice(packet.getAddress(), packet.getPort()).isServer()
                    || ds.getClient(packet.getAddress(), packet.getPort()).getAdmin() == true) {
            }
            client = ds.getClient(packet.getAddress(), packet.getPort());
            ds.deleteClient(client);

            /*
             * Haal alle devices op en stuur een 130 naar alle devipadressices
             * behalve degene die de 130 heeft verstuurd.
             */
            list = ds.getDevices();

            for (int i = 0; i < list.size(); i++) {
                device = (ConnectedDevices) list.get(i);
                /* Stuur niet naar afzender, maar wel als het een admin is */
                if (device.equals(ds.getDevice(packet.getAddress(),
                        packet.getPort())) 
                        && !ds.getClient(packet.getAddress(), packet.getPort()).getAdmin()) {
                    continue;
                }
                creator.msg130(device, msg.getBytes());
            }
        } catch (Exception e) {
            logger.severe("Apparaat niet beschikbaar, handle130");
        }

        return true;
    }

    /**
     * Handelt berichten van type 140. Dit stuurt een pong terug naar de server
     * waarvan net de ping gekregen is.
     *
     * @param msg
     * @param msgarray
     * @param packet
     * @return boolean
     */
    public boolean msg140(String msg, byte[] msgarray, DatagramPacket packet) {
        /*
         * Kijk of in connectedDevices het ipadres voorkomt, dan stuur
         * msg150. Controlserver krijgt apart een message terug
         */
        if (packet.getAddress().equals(Main.csAddress)) {
            creator.msg150(packet.getAddress(), packet.getPort(), msgarray);
            return true;
        } else {
            try {
                device = ds.getDevice(packet.getAddress(), packet.getPort());
                creator.msg150(device, msgarray);
                return true;
            } catch (Exception e) {
                logger.severe("Apparaat zit niet in de lijst, handle104");
            }
        }

        return true;
    }

    /**
     * Handelt berichten van type 150. Dit behandelt een pong die als reactie is
     * gegeven op een verstuurde ping.
     *
     * @param msg
     * @param msgarray
     * @param packet
     * @return boolean
     */
    public boolean msg150(String msg, byte[] msgarray, DatagramPacket packet) {
        try {
            /*
             * Zet de pong tijd van de device op de current tijd.
             */
            device = ds.getDevice(packet.getAddress(), packet.getPort());
            ds.setPongTime(device);
        } catch (Exception e) {
            logger.severe("Apparaat niet bekend, pong kon niet worden"
                    + " opgeslagen, handle150");
        }

        return true;
    }

    /**
     * Handelt berichten van type 160. Dit wijzigt de naam van een client die
     * direct verbonden is met de server en stuurt de wijziging door naar
     * andere ConnectedDevices.
     *
     * @param msg
     * @param msgarray
     * @param packet
     * @return boolean
     */
    public boolean msg160(String msg, byte[] msgarray, DatagramPacket packet) {
        /* Dit bericht kan alleen ontvangen worden van een client. */
        String newname, namesParam, oldname;
        msgSplit = msg.split("\\s+");
        newname = msgSplit[0];
        try {
            /* Haal de client op uit de datastorage */
            client = ds.getClient(packet.getAddress(), packet.getPort());
            /* Check of er dubbelen zijn. */
            if (!ds.checkDuplicateNames(newname)) {
                /* Onthoud de oude naam en zet de nieuwe */
                oldname = client.getName();
                client.setName(newname);
                /* Stuur het succes bericht. */
                creator.msg520(ds.getDevice(client.getIpadress(),
                        client.getPort()), "Het is gelukt!".getBytes());


                /* Maak de string en haal alle devices op. */
                namesParam = oldname + " " + newname;

                /* Stuur de naams verandering op naar iedereen. */
                list = ds.getDevices();
                for (int i = 0; i < list.size(); i++) {
                    device = (ConnectedDevices) list.get(i);
                    creator.msg170(device, namesParam.getBytes());
                }

            } else {
                /* Stuur het faal bericht. */
                creator.msg530(ds.getDevice(client.getIpadress(),
                        client.getPort()), "Naam bestaat al!".getBytes());
            }
        } catch (Exception e) {
            logger.severe("Client niet beschikbaar, handle160");
        }

        return true;
    }

    /**
     * Handelt berichten van type 170. Dit wijzigd de naam van een client die
     * ergens anders op het netwerk verbonden is.
     *
     * @param msg
     * @param msgarray
     * @param packet
     * @return boolean
     */
    public boolean msg170(String msg, byte[] msgarray, DatagramPacket packet) {
        String newname, oldname;

        /* Dit bericht kan alleen ontvangen worden van een server */
        msgSplit = msg.split("\\s+");
        newname = msgSplit[1];
        oldname = msgSplit[0];

        try {
            /* Haal de client op. */
            client = ds.getClient(packet.getAddress(), packet.getPort());
            /* Check of de naam al bestaat */
            if (!ds.checkDuplicateNames(newname)) {
                /* Verander de naam van de cleint */
                client.setName(newname);
                /* Haal de lijst van devices op */
                list = ds.getDevices();
                for (int i = 0; i < list.size(); i++) {
                    device = (ConnectedDevices) list.get(i);
                    /* Niet naar afzender sturen */
                    if (device.equals(ds.getDevice(packet.getAddress(),
                            packet.getPort()))) {
                        continue;
                    }
                    creator.msg170(device, msg.getBytes());
                }
            } else {
                /* Stuur het faal bericht. */
                creator.msg530(ds.getDevice(client.getIpadress(),
                        client.getPort()), "Naam bestaat al!".getBytes());
            }
        } catch (Exception e) {
            logger.severe("Apparaat niet beschikbaar, handle170");
        }

        return true;
    }

    /**
     * Handelt berichten van type 200 en 210. Dit pakt de berichten van de
     * clienten die direct verbonden zijn aan de server en stuurt ze door
     * op het netwerk.
     *
     * @param messagetype
     * @param msg
     * @param msgarray
     * @param packet
     * @return boolean
     */
    public boolean msg2x0(int messagetype, String msg, byte[] msgarray, DatagramPacket packet) {
        msgSplit = msg.split("\\s+", 2);
        String msgParam = "";
        try {
            /* Als er naar iedereen gestuurd moet worden. */
            if (msgSplit[0].matches("#all")) {
                logger.info("Bericht naar all.");
                /*
                 * Stuur naar alle devices
                 */
                list = ds.getDevices();
                for (int i = 0; i < list.size(); i++) {
                    device = (ConnectedDevices) list.get(i);
                    /* Maak de string. */
                    if (ds.getClient(packet.getAddress(),
                            packet.getPort()).getAdmin()) {
                        Clients tClient = ds.getClient(packet.getAddress(), packet.getPort());
                        msgParam = tClient.getName() + tClient.getIpadress() + tClient.getPort()
                                + " " + "#all " + msgSplit[1];

                    } else {
                        msgParam = ds.getClient(packet.getAddress(),
                                packet.getPort()).getName()
                                + " " + "#all " + msgSplit[1];
                    }
                    /* Als het ontvangen bericht een 200 stuur een 300
                     * als het een 210 stuur een 310
                     */
                    /*deze is voor de admin*/
                    if ((messagetype == 200 ||messagetype == 210)
                            && ds.getClient(packet.getAddress(), packet.getPort()).getAdmin()) {
                        creator.msg310(device, msgParam.getBytes());

                    } /*voor geen admin*/ 
                    else if (messagetype == 200) {
                        creator.msg300(device, msgParam.getBytes());
                    } else {
                        creator.msg310(device, msgParam.getBytes());
                    }

                }
            } /* Als het een private message is */
            else {
                /*naamverzender naamontvanger bericht*/
                msgParam = ds.getClient(packet.getAddress(),
                        packet.getPort()).getName()
                        + " " + msgSplit[0] + " " + msgSplit[1];
                client = ds.getClient(msgSplit[0]);
                /* Als het ontvangen bericht een 200 stuur een 300
                 * als het een 210 stuur een 310
                 */
                if (messagetype == 200) {
                    /*stuur het bericht naar de ontvanger*/
                    creator.msg300(ds.getDevice(client.getIpadress(),
                            client.getPort()), msgParam.getBytes());
                    /*stuur het bericht ook terug naar de verzender*/
                    creator.msg300(ds.getDevice(packet.getAddress(),
                    		packet.getPort()),msgParam.getBytes());
                    sendToAllAdmins(messagetype, msgParam);
                } else {
                    creator.msg310(ds.getDevice(client.getIpadress(),
                            client.getPort()), msgParam.getBytes());
                    /*stuur het bericht ook terug naar de verzender*/
                    creator.msg310(ds.getDevice(packet.getAddress(),
                    		packet.getPort()),msgParam.getBytes());
                    sendToAllAdmins(messagetype, msgParam);
                }

                logger.info("Bericht naar client: " + client.getName());
            }
        } catch (Exception e) {
            logger.severe("Apparaat kon niet gevonden worden, handl 200/210");
        }

        return true;
    }

    /**
     * Handelt berichten van type 300 en 310. Dit stuurt berichten die ergens
     * anders op het netwerk zijn verstuurd door.
     *
     * @param messagetype
     * @param msg
     * @param msgarray
     * @param packet
     * @return boolean
     */
    public boolean msg3x0(int messagetype, String msg, byte[] msgarray, DatagramPacket packet) {
        msgSplit = msg.split("\\s+", 3);

        try {
            /* Als het bericht naar iedereen gestuur moet worden */
            if (msgSplit[1].matches("#all")) {
                logger.info("Bericht naar all.");
                /* Haal alle devices op en stuur het naar iedereen 
                 * behalve de afzender.
                 */
                list = ds.getDevices();                	
                for (int i = 0; i < list.size(); i++) {
                    ConnectedDevices tDevice = (ConnectedDevices) list.get(i);
                    /* Stuur niet naar de afzender. */
                    if (tDevice.equals(ds.getDevice(packet.getAddress(),
                            packet.getPort()))) {
                        continue;
                    }

                    /* Stuur het bericht door zoals hij is met de zelfde
                     * messagetype. 
                     */
                    if (messagetype == 300) {
                        creator.msg300(tDevice,msg.getBytes());                
                    } else {
                        creator.msg310(tDevice, msg.getBytes());
                    }
                }

            } /* Als het een private message is. */
            else {
                client = ds.getClient(msgSplit[1]);
                device = ds.getDevice(client.getIpadress(), client.getPort());
                /* Stuur het bericht door de naar de debetreffende
                 * device.
                 */
                if (messagetype == 300) {
                    sendToAllAdmins(messagetype, msg);
                    creator.msg300(device,                	 msg.getBytes());
                    

                } else {
                    sendToAllAdmins(messagetype, msg);
                    creator.msg310(device, msg.getBytes());
                    /*stuur het bericht ook terug naar de verzender*/

                }

                logger.info("Bericht naar apparaat: "
                        + device.getIpadress());
            }
        } catch (Exception e) {
            logger.severe("Apparaat niet beschikbaar, handle 300/310");
        }

        return true;
    }

    /**
     * Handelt berichten van type 600. Dit behandelt een aanvraag van een server
     * om deze server als parent te hebben.
     *
     * @param msg
     * @param msgarray
     * @param packet
     * @return boolean
     */
    public boolean msg600(String msg, byte[] msgarray, DatagramPacket packet) {
        /* maak een device aan voor het kind. */
        device = new ConnectedDevices(packet.getAddress(), packet.getPort());

        try {
            /* Set de tag van je */
            device.setTag(msg);
            ds.insertDevice(device);
            /* Haal de clients op die niet aan de net verbonden server 
             * op dit voorkomt dat je de net verkregen clienten van de 
             * kind server terug stuurt. 
             */
            list = ds.getActiveClients();

            /* Wij hebben geen clients om te sturen dus sturen
             * en lege lijst.
             */
            if (list.isEmpty()) {
                msg = "000000000000";
                creator.msg110(device, msg.getBytes());
                logger.info("Geen kinderen te versturen");
            } /* We hebben wel clienten en sturen alle clienten naar het
             * kind.
             */
            else {
                logger.info("Ik ga al mijn clients sturen naar mijn parent");
                for (int i = 0; i < list.size(); i++) {
                    client = (Clients) list.get(i);
                    temp = String.format("%06d", list.size());
                    temp = String.format("%06d", i + 1) + temp
                            + client.getName();
                    /* Stuur 110 totdat er geen clienten meer zijn */
                    creator.msg110(ds.getDevice(client.getIpadress(),
                            client.getPort()), temp.getBytes());

                }
                logger.info("Heb kinderen verstuurd.");
            }
        } catch (Exception e) {
            logger.severe("Apparaat niet beschikbaar, handle600");
        }

        return true;
    }

    /**
     * Handelt berichten van type 602. Dit behandelt een antwoord van de
     * control-server op een verzoek om aan het netwerk deel te nemen.
     *
     * @param msg
     * @param msgarray
     * @param packet
     * @return boolean
     */
    public boolean msg602(String msg, byte[] msgarray, DatagramPacket packet) {
        /* De root heeft me geen parent toegewezen dus wordt ik zelf root. */
        if (msg.matches("none")) {
            ds.root = true;
            logger.info("Yay ik ben root!");
        } /* Als er een server wordt toegewezen. */ else {
            /* Parse de string */
            msgSplit = msg.split(":");
            port = Integer.parseInt(msgSplit[1]);
            try {
                address = InetAddress.getByName(msgSplit[0]);
                /* Maak de connected device aan voor je parent. */
                device = new ConnectedDevices(address, port);
                /* Set zijn naam en maak de parent veld true. */
                device.setTag(msgSplit[2]);
                device.setParent(true);
                ds.setParent(msgSplit[2]);
                /*stop de device in de array.*/
                ds.insertDevice(device);

                /* Stuur daadwerklijk het bericht op en geef mijn eigen
                 * tag mee.
                 */
                creator.msg600(device, Main.ident.getBytes());
                logger.info("ik ben nu aan deze server verbonden "
                        + ((ConnectedDevices) ds.getDevices().get(0)).getIpadress()
                        + " "
                        + ((ConnectedDevices) ds.getDevices().get(0)).getTag());
                /* Haal de clients op die niet aan de net verbonden 
                 * server op dit voorkomt dat je de net verkregen 
                 * clienten van de kind server terug stuurt.
                 */ System.out.println("3 ");

                list = ds.getActiveClients();
                /* Als de lijst leeg is stuur je een lege lijst op */
                if (list.isEmpty()) {
                    msg = "000000000000";
                    creator.msg110(device, msg.getBytes());
                    logger.info("Ik stuur een lege lijst met clienten");

                } /* Als de lijst niet leeg is */ else {
                    logger.info("Ik ga al mijn clients sturen naar mijn parent");
                    for (int i = 0; i < list.size(); i++) {
                        client = (Clients) list.get(i);
                        temp = String.format("%06d", list.size());
                        temp = String.format("%06d", i + 1) + temp
                                + client.getName();
                        creator.msg110(ds.getDevice(client.getIpadress(),
                                client.getPort()), temp.getBytes());
                    }
                }
            } catch (Exception ex) {
                logger.severe("Error in handling 602, unknown host:"
                        + ex.getMessage());
            }
        }

        return true;
    }

    /**
     * Handelt berichten van type 604. Dit behandelt het uitvallen van servers
     * op het netwerk.
     *
     * @param msg
     * @param msgarray
     * @param packet
     * @return boolean
     */
    public boolean msg604(String msg, byte[] msgarray, DatagramPacket packet) {
        msgSplit = msg.split("\\s+");
        logger.warning("Ben binnen 604.");

        // kijk of server aan je zit, verwijder hem dan en stuur 130 naar
        // iedereen
        /* Als de je maar 1 paramter krijgt. Dus kind moet verwijderd worden */
        if (msgSplit.length == 1) {
            logger.warning("Er is maar 1 parameter.");

            try {
                /* Haal de dode device op */
                device = ds.getDevice(msg);
                /* Haal de lijst op met alle clienten vast aan deze server */
                list = ds.getClientsConnectedToServer(device.getIpadress(),
                        device.getPort());
                /* Verwijder alle alle client moet misschien veranderen
                 * naar ongeldig maken. bijna hetzelfde.
                 */
                for (int i = 0; i < list.size(); i++) {
                    client = (Clients) list.get(i);
                    name = client.getName();

                    /* Stuur dood bericht naar alle apparaten */
                    List list2 = ds.getDevices();
                    for (int j = 0; j < list2.size(); j++) {
                        ConnectedDevices tDevice =
                                (ConnectedDevices) list.get(i);
                        creator.msg130(tDevice, name.getBytes());
                    }

                    ds.deleteClient(client);
                }

                /* Delete het kind device */
                ds.deleteDevice(device);
            } catch (Exception e) {
                logger.severe("Device was al verwijderd/ niet direct "
                        + "verbonden");
            }

        } /* Als er twee parameters worden gegeven. */ else if (msgSplit.length == 2) {
            logger.warning("Er zijn 2 parameters.");
            /* Als je de nieuwe root wordt. */
            if (msgSplit[1].matches("none")) {
                /* Als root aangewezen door CS */
                logger.warning("Ik heb NONE uitgelezen, Ik ben root.");
                /* Maak het parentveld leeg. */
                ds.setParent("");
                ds.root = true;

                try {
                    /* Haal je parent uit de datastructuur */
                    device = ds.getDevice(msgSplit[0]);
                    /* Haal alle clieten op die aan je parent vast zitten */
                    list = ds.getClientsConnectedToServer(device.getIpadress(), device.getPort());
                    /* Delete alle clienten die aan hem vast zitten */
                    for (int i = 0; i < list.size(); i++) {
                        client = (Clients) list.get(i);
                        name = client.getName();
                        logger.info("ik ga deze verwijderen " + name);
                        List list2 = ds.getDevices();
                        for (int j = 0; j < list2.size(); j++) {
                            ConnectedDevices tDevice =
                                    (ConnectedDevices) list.get(i);
                            logger.info("IK STUUR NU 130");
                            creator.msg130(tDevice, name.getBytes());
                        }
                        ds.deleteClient(client);
                    }

                    logger.info("Clients van dode server zijn verwijderd.");
                    /*delete de device zelf.*/
                    ds.deleteDevice(device);
                } catch (Exception e) {
                    logger.severe("Couldn't remove device");
                }
                /*maak jezelf root.*/
                ds.root = true;
                return true;

            } /*Als je zelf je bent verwijderd door iemand.*/
            else if (msgSplit[1].matches("self")) {

                logger.warning("Ik heb SELF uitgelezen.");

                /*stuur een 701 naar alle connected devices, omdat je dissconnected
                bent, maar dit is inprincipe dubbel op, want de 701 wordt naar
                iedereen gestuurd*/
                list = ds.getDevices();
                for (int i = 0; i < list.size(); i++) {
                    device = (ConnectedDevices) list.get(i);
                    creator.msg701(device, 
                        "Ik ben gedisconnect door de controlserver".getBytes());
                }

                /*maak al je data structuren leeg en zet alles op
                de default waardes.*/
                ds.flushClients();
                ds.flushDevices();
                ds.setParent("");
                ds.root = false;

                /*maak een nieuw server ident aan.*/
                Main.makeServerIdent();
                /*vraag een nieuwe parent aan bij de controlserver*/
                creator.msg601(Main.csAddress, Main.csPort, Main.ident.getBytes());
            }
            /*Wanneer er een nieuwe server wordt aangewezen.*/
            else {
                try {

                    ConnectedDevices oldDevice = ds.getDevice(msgSplit[0]);
                    /* Connect naar nieuwe server */
                    logger.info("Ik heb een nieuwe server gekregen om mesee "
                            + "te connecten");
                    String[] newServer = msgSplit[1].split(":");
                    /*Parsen van je nieuwe parent en maak de device aan
                    en voeg hem toe aan de datastorage.*/
                    device = new ConnectedDevices(InetAddress.getByName(newServer[0])
                            , Integer.parseInt(newServer[1]));
                    device.setTag(newServer[2]);
                    ds.setParent(newServer[2]);
                    ds.insertDevice(device);

                    /*verwijder alle clienten die aan hem vast zaten uit
                    je eige lijst.*/
                    list = ds.getClientsConnectedToServer(oldDevice.getIpadress()
                            , oldDevice.getPort());
                    /*delete alle clienten die aan hem vast zitten*/
                    for (int i = 0; i < list.size(); i++) {
                        client = (Clients) list.get(i);
                        /*haal alle devices op*/
                        List list2 = ds.getDevices();
                        for (int j = 0; j < list2.size(); j++) {
                            ConnectedDevices tDevice =
                                    (ConnectedDevices) list.get(i);
                            logger.info("IK STUUR NU 130");
                            /*
                             *laat alle andere servers weten dat de client
                             *weg is
                             */
                            creator.msg130(tDevice, name.getBytes());
                        }
                        ds.deleteClient(client);

                    }
                    /* Meld jezelf aan bij de nieuwe parent.*/
                    creator.msg600(device, Main.ident.getBytes());

                    /*haal de clients op die niet aan de net verbonden server op
                    dit voorkomt dat je de net verkregen clienten van de kind
                    server terug stuurt.*/
                    list = ds.getActiveClients();;
                    /*Als je eige lijst leeg is*/
                    if (list.isEmpty()) {
                        msg = "000000000000";
                        creator.msg110(device, msg.getBytes());
                        logger.info("Ik stuur een lege lijst met clienten");
                    } /*Als de lijst niet leeg is.*/ else {
                        logger.info("Ik ga al mijn clients sturen naar mijn parent");
                        for (int i = 0; i < list.size(); i++) {
                            client = (Clients) list.get(i);
                            if (!ds.getDevice(client.getIpadress(),
                                    client.getPort()).equals(device)) {
                                temp = String.format("%06d", list.size());
                                temp = String.format("%06d", i + 1) + temp
                                        + client.getName();
                                creator.msg110(ds.getDevice(client.getIpadress(), client.getPort()),
                                        temp.getBytes());
                            }
                        }
                    }
                } catch (Exception e) {
                    logger.severe("Apparaat niet beschikbaar, handle604");
                }
            }

        }

        return true;
    }

    /**
     * Handelt berichten van type 700. Dit behandelt een aanvraag van een
     * admin om een server te verwijderen uit het netwerk.
     *
     * @param msg
     * @param msgarray
     * @param packet
     * @return boolean
     */
    public boolean msg700(String msg, byte[] msgarray, DatagramPacket packet) {
        try {
            client = ds.getClient(packet.getAddress(), packet.getPort());
            if (client.getAdmin()) {
                device = ds.getDevice(msg);
                creator.msg603(device.getIpadress(), device.getPort(), msgarray);
            } else {
                logger.severe("Admin command given a by non-admin");
            }
        } catch (Exception e) {
            logger.severe("Apparaat niet beschikbaar, handle700");
        }


        return true;
    }

    /**
     * Handelt berichten van type 701. Dit behandelt een aankondiging van
     * een server die het netwerk verlaat.
     *
     * @param msg
     * @param msgarray
     * @param packet
     * @return boolean
     */
    public boolean msg701(String msg, byte[] msgarray, DatagramPacket packet) {
        try {
            device = ds.getDevice(packet.getAddress(), packet.getPort());
            list = ds.getClientsConnectedToServer(device.getIpadress(),
                    device.getPort());

            for (int i = 0; i < list.size(); i++) {
                client = (Clients) list.get(i);
                ds.deleteClient(client);
            }

            ds.deleteDevice(device);
            logger.warning("701, Apparaat stopt");
        } catch (Exception e) {
            logger.severe("Kan client niet verwijderen, handle701");
        }

        return true;
    }

    /**
     * MessageHandler thread die inkomende berichten afhandelt.
     */
    public void run() {
        byte[] data;
        int messagelength, messagetype, messagenumber, oldmessagenumber = 0, pMessagenumber = -1;
        /*
         * We beginnen met het sturen van een bericht naar de controlserver, om
         * een plaats in de aan gewezen te krijgen. We beginnen meteen met
         * luisteren in de Listener thread, want we kunnen toch geen berichten
         * ontvangen die niet aan ons zijn gericht.
         */
        creator.msg601(Main.csAddress, Main.csPort, Main.ident.getBytes());

        try {
            /*
             * Totdat de thread wordt gestopt blijven we berichten uit de queue
             * te halen. Deze worden in de handel fucntie aan behandeld en
             * afgewerkt. De functies die worden gebruikt worden voor het
             * versturen staan in de messagecreator.
             */
            while (!isInterrupted()) {
                DatagramPacket received = getMessageFromMessageQueue();
                data = received.getData();
                logger.info("sent by" + received.getAddress());
                messagetype = ((data[2] << 8) & 0xff00) | (data[3] & 0xff);
                messagenumber = ((data[4] << 8) & 0xff00) | (data[5] & 0xff);

                if (handle(messagetype, received)) {
                    pMessagenumber = messagenumber;
                } else {
                    logger.severe("Error in de handle functie, "
                            + "ik breek eruit.");
                    list = ds.getDevices();
                    for (int i = 0; i < list.size(); i++) {
                        device = (ConnectedDevices) list.get(i);
                        creator.msg701(device, "Foutieve bericht ontvangen".getBytes());
                    }
                    break;
                }

            }
        } catch (Exception ex) {
            logger.severe("Er ging iets fout in de messagehandler");
        }
    }
}
